package com.huiquan.vocab.utils;


import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;

import org.ansj.domain.Term;
import org.ansj.splitWord.analysis.ToAnalysis;


/**
 * 调用word2vec的结果进行词相似的判断.
 *
 * @author renhao.cao.
 *         Created 2014-8-20.
 */
public class Word2VecAlgo {

	private static HashMap<String, float[]> wordMap = new HashMap<String, float[]>();
	
	/** 词典模型中词的数量*/
	private static int words;
	
	/** 词典模型中词的表示向量维度*/
	private static int size;
	
	/** 近义词列表的数量*/
	private static int topNSize = 40;
	
//	private static String path = "./data/xywy.bin";
	private static String path = "/root/data/symptom_relation_file/brzs_fzl.bin";
//	private static String path = "./data/vectorssghs.bin";
	
	/**
	 * 加载模型
	 * 
	 * @param path 模型的路径
	 * @throws IOException
	 */
	static {
		DataInputStream dis = null;
		BufferedInputStream bis = null;
		double len = 0;
		float vector = 0;
		try {
			bis = new BufferedInputStream(new FileInputStream(path));
			dis = new DataInputStream(bis);
			//读取词数
			words = Integer.parseInt(readString(dis));
			//大小
			size = Integer.parseInt(readString(dis));

			String word;
			float[] vectors = null;
			for (int i = 0; i < words; i++) {
				word = readString(dis);
				if("15天".equals(word)){
					System.out.println(111);
				}
				vectors = new float[size];
				len = 0;
				for (int j = 0; j < size; j++) {
					vector = readFloat(dis);
					len += vector * vector;
					vectors[j] = (float) vector;
				}
				len = Math.sqrt(len);

				for (int j = 0; j < vectors.length; j++) {
					vectors[j] = (float) (vectors[j] / len);
				}
				wordMap.put(word, vectors);
				dis.read();
			}
		} catch(Exception e) {
			e.printStackTrace();
		} finally {
			try {
				bis.close();
				dis.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			
		}
	}
	
	private static final int MAX_SIZE = 50;

	/**
	 * 获取一个词的近义词列表
	 * 
	 * 		通过遍历该词与所有词进行相似度计算，然后排序得到结果
	 * 
	 * @param word 输入词
	 * @return 词列表及相似度值
	 */
	public Set<WordEntry> distance(String word) {
		float[] wordVector = getWordVector(word);
		if (wordVector == null) {
			return null;
		}
		Set<Entry<String, float[]>> entrySet = wordMap.entrySet();
		float[] tempVector = null;
		List<WordEntry> wordEntrys = new ArrayList<WordEntry>(topNSize);
		String name = null;
		for (Entry<String, float[]> entry : entrySet) {
			name = entry.getKey();
			if (name.equals(word)) {
				continue;
			}
			float dist = 0;
			tempVector = entry.getValue();
			for (int i = 0; i < wordVector.length; i++) {
				dist += wordVector[i] * tempVector[i];
			}
			insertTopN(name, dist, wordEntrys);
		}
		return new TreeSet<WordEntry>(wordEntrys);
	}
	
	/**
	 * 获取一句话的近义词列表
	 * 
	 * 		通过遍历该词与所有词进行相似度计算，然后排序得到结果
	 * 
	 * @param word 输入词
	 * @return 词列表及相似度值
	 */
	public Set<WordEntry> distanceSentence(String word) {
		float[] wordVector = getSentenceVector(word);
		if (wordVector == null) {
			return null;
		}
		Set<Entry<String, float[]>> entrySet = wordMap.entrySet();
		float[] tempVector = null;
		List<WordEntry> wordEntrys = new ArrayList<WordEntry>(topNSize);
		String name = null;
		for (Entry<String, float[]> entry : entrySet) {
			name = entry.getKey();
			if (name.equals(word)) {
				continue;
			}
			float dist = 0;
			tempVector = entry.getValue();
			for (int i = 0; i < wordVector.length; i++) {
				dist += wordVector[i] * tempVector[i];
			}
			insertTopN(name, dist, wordEntrys);
		}
		return new TreeSet<WordEntry>(wordEntrys);
	}

	/**
	 * 近义词
	 * 
	 * @param word0 
	 * @param word1 
	 * @param word2 
	 * 
	 * @return
	 */
	public TreeSet<WordEntry> analogy(String word0, String word1, String word2) {
		float[] wv0 = getWordVector(word0);
		float[] wv1 = getWordVector(word1);
		float[] wv2 = getWordVector(word2);

		if (wv1 == null || wv2 == null || wv0 == null) {
			return null;
		}
		float[] wordVector = new float[size];
		for (int i = 0; i < size; i++) {
			wordVector[i] = wv1[i] - wv0[i] + wv2[i];
		}
		float[] tempVector;
		String name;
		List<WordEntry> wordEntrys = new ArrayList<WordEntry>(topNSize);
		for (Entry<String, float[]> entry : wordMap.entrySet()) {
			name = entry.getKey();
			if (name.equals(word0) || name.equals(word1) || name.equals(word2)) {
				continue;
			}
			float dist = 0;
			tempVector = entry.getValue();
			for (int i = 0; i < wordVector.length; i++) {
				dist += wordVector[i] * tempVector[i];
			}
			insertTopN(name, dist, wordEntrys);
		}
		return new TreeSet<WordEntry>(wordEntrys);
	}

	/**
	 * 计算两个词的相似度.
	 *
	 * @param str1
	 * @param str2
	 * @return 取4位小数的相似度值
	 */
	public static double similarity(String str1, String str2) {
		float[] feat1 = getWordVector(str1);
		float[] feat2 = getWordVector(str2);
		if(feat1 == null || feat2 == null)
			return 0;
		
		double sum1 = 0;
		double sum2 = 0;
		double sum3 = 0;
		for(int i = 0;i < feat1.length;i++){
			sum1 = sum1 + feat1[i] * feat1[i];
			sum2 = sum2 + feat2[i] * feat2[i];
			sum3 = sum3 + feat1[i] * feat2[i];
		}
		sum1 = Math.sqrt(sum1);
		sum2 = Math.sqrt(sum2);
		double cos = sum3/(sum1*sum2);
		cos = Math.ceil(cos*10000)/10000;
		
		return cos;
	}
	
	/**
	 * 计算两个句子的相似度.
	 *
	 * @param str1
	 * @param str2
	 * @return 取4位小数的相似度值
	 */
	public static double similaritySentence(String sentence1, String sentence2) {
		float[] feat1 = getSentenceVector(sentence1);
		float[] feat2 = getSentenceVector(sentence2);
		if(feat1 == null || feat2 == null)
			return 0;
		
		double sum1 = 0;
		double sum2 = 0;
		double sum3 = 0;
		for(int i = 0;i < feat1.length;i++){
			sum1 = sum1 + feat1[i] * feat1[i];
			sum2 = sum2 + feat2[i] * feat2[i];
			sum3 = sum3 + feat1[i] * feat2[i];
		}
		sum1 = Math.sqrt(sum1);
		sum2 = Math.sqrt(sum2);
		double cos = sum3/(sum1*sum2);
		cos = Math.ceil(cos*10000)/10000;
		
		return cos;
	}
	
	private void insertTopN(String name, float score,
			List<WordEntry> wordsEntrys) {
		if (wordsEntrys.size() < topNSize) {
			wordsEntrys.add(new WordEntry(name, score));
			return;
		}
		float min = Float.MAX_VALUE;
		int minOffe = 0;
		for (int i = 0; i < topNSize; i++) {
			WordEntry wordEntry = wordsEntrys.get(i);
			if (min > wordEntry.score) {
				min = wordEntry.score;
				minOffe = i;
			}
		}

		if (score > min) {
			wordsEntrys.set(minOffe, new WordEntry(name, score));
		}

	}

	public class WordEntry implements Comparable<WordEntry> {
		public String name;
		public float score;

		public WordEntry(String name, float score) {
			this.name = name;
			this.score = score;
		}

		@Override
		public String toString() {
			return this.name + "\t" + score;
		}

		@Override
		public int compareTo(WordEntry o) {
			if (this.score > o.score) {
				return -1;
			} else {
				return 1;
			}
		}

	}

	/**
	 * 得到词向量
	 * 
	 * @param word
	 * @return
	 */
	public static float[] getWordVector(String word) {
		return wordMap.get(word);
	}
	
	/**
	 * 获取句子向量.
	 * 
	 * @param sentence
	 * @return
	 */
	public static float[] getSentenceVector(String sentence) {
		List<Term> terms = ToAnalysis.parse(sentence).getTerms();
		float[] vecSentence = new float[size];
		for(Term term : terms) {
			float[] vec = getWordVector(term.getRealName());
			for(int i = 0; i < size; i++) {
				float value = vec == null ? 0 : vec[i];
				vecSentence[i] = vecSentence[i] + value;
			}
		}
		
		return vecSentence;
	}

	public static float readFloat(InputStream is) throws IOException {
		byte[] bytes = new byte[4];
		is.read(bytes);
		return getFloat(bytes);
	}

	/**
	 * 读取一个float
	 * 
	 * @param b
	 * @return
	 */
	public static float getFloat(byte[] b) {
		int accum = 0;
		accum = accum | (b[0] & 0xff) << 0;
		accum = accum | (b[1] & 0xff) << 8;
		accum = accum | (b[2] & 0xff) << 16;
		accum = accum | (b[3] & 0xff) << 24;
		return Float.intBitsToFloat(accum);
	}
	
	/**
	 * 读取一个字符串
	 * 
	 * @param dis
	 * @return 
	 * @throws IOException
	 */
	private static String readString(DataInputStream dis) throws IOException {
		byte[] bytes = new byte[MAX_SIZE];
		byte b = dis.readByte();
		int i = -1;
		StringBuilder sb = new StringBuilder();
		while (b != 32 && b != 10) {
			i++;
			bytes[i] = b;
			b = dis.readByte();
			if (i == 49) {
				sb.append(new String(bytes));
				i = -1;
				bytes = new byte[MAX_SIZE];
			}
		}
		sb.append(new String(bytes, 0, i + 1));
		return sb.toString();
	}

	public int getTopNSize() {
		return topNSize;
	}


	public HashMap<String, float[]> getWordMap() {
		return wordMap;
	}

	public int getWords() {
		return words;
	}
	
	public int getSize() {
		return size;
	}
	
	/**
	 * TODO Put here a description of what this method does.
	 * 
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
//		System.out.println(Word2VecAlgo.words + ":" + Word2VecAlgo.size);
//		System.out.println("Two word sim count");
//		String str1 = "腹痛";
//		String str2 = "轻微腹痛";
//		double sim = Word2VecAlgo.similarity(str1, str2);
//		System.out.println(str1 + "-" + str2 + ":" + sim);
		
		System.out.println("Two sentence sim count");
		String str1 = "下肢体";
		String str2 = "下肢";
		double sim = Word2VecAlgo.similaritySentence(str1, str2);
		System.out.println(str1 + "-" + str2 + ":" + sim);
		
		
		
//		PrintWriter pw = new PrintWriter(new File("./data/vec.txt"));
//		for(Entry<String, float[]> entry : wordMap.entrySet()) {
//			pw.println(entry.getKey() + "\t" + StrUtil.join(entry.getValue(), ","));
//		}
//		IOUtil.forceClose(pw);
		
		
//		System.out.println(wordMap.containsKey("综合"));
		
//		Word2VecAlgo vec = new Word2VecAlgo();
//		System.out.println("*******************************");
//		System.out.println("One word analysis");
//		Set<WordEntry> result = new TreeSet<WordEntry>();
//		result = vec.distance("乌青");
//		Iterator<WordEntry> iter = result.iterator();
//		while (iter.hasNext()) {
//			WordEntry word = iter.next();
//			System.out.println(word.name + " " + word.score);
//		}
		/*
		System.out.println("*******************************");
		System.out.println("Three word analysis");
		result = vec.analogy("小米", "华为", "手机");
		iter = result.iterator();
		while (iter.hasNext()) {
			WordEntry word = (WordEntry) iter.next();
			System.out.println(word.name + " " + word.score);
		}
		*/
	}
	
}